use anyhow::Result;
use axum::{
    extract::{DefaultBodyLimit, State},
    http::Method,
    response::{Html, Json},
    routing::{delete, get, post, put},
    Router,
};
use serde_json::json;
use std::net::SocketAddr;
use std::sync::Arc;

use tower::ServiceBuilder;
use tower_http::{
    cors::{Any, CorsLayer},
    services::ServeDir,
    trace::TraceLayer,
};
use tracing::info;
use tracing_subscriber::{self, layer::SubscriberExt, util::SubscriberInitExt};

mod config;
// mod database; // Removed after SeaORM migration
mod entity;
mod error;
mod handlers;
mod migration;
// mod middleware; // Removed after SeaORM migration
mod models;
mod services;
mod utils;

use config::AppConfig;
use handlers::{admin, auth, data, dvc_remote, lineage};
use sea_orm::{Database, DatabaseConnection};
use sea_orm_migration::MigratorTrait;
use services::{
    AuthService, DataService, DvcMetadataService, DvcService, LineageService, StorageService,
};

#[derive(Clone)]
pub struct AppState {
    pub auth_service: Arc<AuthService>,
    pub data_service: Arc<DataService>,
    pub storage_service: Arc<StorageService>,
    pub dvc_service: Arc<DvcService>,
    pub dvc_metadata_service: Arc<DvcMetadataService>,
    pub lineage_service: Arc<LineageService>,
    pub db: DatabaseConnection,
    pub config: AppConfig,
}

async fn get_config(State(app_state): State<AppState>) -> Json<serde_json::Value> {
    Json(json!({
        "dvc_remote_url": app_state.config.get_dvc_remote_url()
    }))
}

#[tokio::main]
async fn main() -> Result<()> {
    // Initialize tracing with both console and file output
    let logs_dir = "logs";
    std::fs::create_dir_all(logs_dir).ok();

    let file_appender = tracing_appender::rolling::daily(logs_dir, "app.log");
    let (non_blocking, _guard) = tracing_appender::non_blocking(std::io::stdout());

    tracing_subscriber::registry()
        .with(
            tracing_subscriber::fmt::layer()
                .with_writer(file_appender)
                .with_ansi(false)
                .with_level(true)
                .with_target(false),
        )
        .with(
            tracing_subscriber::fmt::layer()
                .with_writer(non_blocking)
                .with_ansi(true)
                .with_level(true)
                .with_target(false),
        )
        .with(tracing_subscriber::filter::EnvFilter::from_default_env())
        .init();

    // Load .env file
    dotenvy::dotenv().ok();

    // Load configuration
    let config = AppConfig::from_env()?;
    info!(
        "Starting RINT Data Manager on {}:{}",
        config.server.host, config.server.port
    );

    // Initialize database
    let db = Database::connect(&config.database.url).await?;

    // Run migrations
    migration::Migrator::up(&db, None).await?;

    // Initialize services
    let auth_service = Arc::new(AuthService::new(db.clone(), config.clone()));
    let data_service = Arc::new(DataService::new(db.clone()));
    let storage_service = Arc::new(StorageService::new(config.dvc.storage_path.clone()));
    let dvc_service = Arc::new(DvcService::new(db.clone()));
    let dvc_metadata_service = Arc::new(DvcMetadataService::new(db.clone()));
    let lineage_service = Arc::new(LineageService::new(db.clone()));

    let app_state = AppState {
        auth_service,
        data_service,
        storage_service,
        dvc_service,
        dvc_metadata_service,
        lineage_service,
        db: db.clone(),
        config: config.clone(),
    };

    // Public routes (no authentication required)
    let public_routes = Router::new()
        // Public HTML pages
        .route(
            "/",
            get(|| async { Html(include_str!("../frontend/index.html")) }),
        )
        .route(
            "/login",
            get(|| async { Html(include_str!("../frontend/login.html")) }),
        )
        .route(
            "/register",
            get(|| async { Html(include_str!("../frontend/register.html")) }),
        )
        .route(
            "/register-admin",
            get(|| async { Html(include_str!("../frontend/register-admin.html")) }),
        )
        // Public API routes
        .route("/api/register", post(auth::register))
        .route("/api/register-admin", post(auth::register_admin))
        .route("/api/login", post(auth::login))
        .route("/api/data/public", get(data::list_public_data_items))
        .route("/api/config", get(get_config))
        .route("/api/data/upload-metadata", post(data::upload_dvc_metadata))
        // Static files
        .nest_service("/static", ServeDir::new("static"));

    // Protected routes (authentication required)
    let protected_routes = Router::new()
        // Protected HTML pages
        .route(
            "/dashboard",
            get(|| async { Html(include_str!("../frontend/dashboard.html")) }),
        )
        .route(
            "/admin",
            get(|| async { Html(include_str!("../frontend/admin.html")) }),
        )
        // Protected API routes
        .route("/api/user-info", get(auth::user_info))
        .route("/api/data", get(data::list_data_items))
        .route("/api/data/upload", post(data::upload_data))
        .route("/api/data/:id", get(data::get_data_item))
        .route("/api/data/:id", delete(data::delete_data_item))
        .route("/api/data/:id/download", get(data::download_data_file))
        .route("/api/data/:id/dvc", get(data::download_dvc_file))
        .route("/api/data/:id/dvc/content", get(data::get_dvc_content))
        .route("/api/data/:id/metadata", put(data::upload_metadata))
        .route("/api/data/:id/public", put(data::make_data_public))
        // Admin routes (protected)
        .route("/api/admin/users", get(admin::list_users))
        .route("/api/admin/users/:id", get(admin::get_user))
        .route(
            "/api/admin/users/:id/toggle-admin",
            post(admin::toggle_admin_status),
        )
        .route("/api/admin/users/:id", delete(admin::delete_user))
        .route(
            "/api/admin/users/:id/reset-password",
            post(admin::reset_user_password),
        )
        .route("/api/admin/data/:id", delete(admin::admin_delete_data_item))
        .route("/api/admin/data/public", get(admin::list_public_data))
        .route("/api/admin/data/analyze", get(admin::analyze_data_safety))
        .route(
            "/api/admin/data/bulk-delete",
            post(admin::bulk_delete_safe_data),
        )
        .route("/api/admin/garbage-collect", post(admin::garbage_collect))
        // DVC remote routes (protected - support both JWT and basic auth)
        .route("/api/dvc/files", get(dvc_remote::dvc_list_files))
        .route("/api/dvc/file/*path", get(dvc_remote::dvc_get_file_info))
        .route(
            "/api/dvc/remote/*path",
            axum::routing::any(dvc_remote::handle_dvc_request),
        )
        .route(
            "/api/dvc/cache/*hash_path",
            axum::routing::any(dvc_remote::handle_dvc_cache_get),
        )
        // Lineage tracking routes (protected)
        .route(
            "/api/lineage/relationships",
            get(lineage::get_lineage_relationships),
        )
        .route("/api/lineage/item/:id", get(lineage::get_data_item_lineage))
        .route("/api/lineage", post(lineage::create_lineage_relationship))
        .route(
            "/api/lineage/:id",
            put(lineage::update_lineage_relationship),
        )
        .route(
            "/api/lineage/:id",
            delete(lineage::delete_lineage_relationship),
        )
        .route(
            "/api/lineage/graph",
            get(lineage::get_lineage_relationships),
        ) // Placeholder function
        .route("/api/lineage/stats", get(lineage::get_lineage_stats))
        .route(
            "/api/lineage/suggestions",
            get(lineage::get_lineage_suggestions),
        )
        .route(
            "/api/lineage/suggestions/process",
            post(lineage::process_lineage_suggestion),
        )
        .route(
            "/api/lineage/detect/:id",
            post(lineage::auto_detect_lineage),
        )
        .route(
            "/api/lineage/paths/:from_id/:to_id",
            get(lineage::get_lineage_paths),
        );

    // Note: Authentication is now handled by the AuthenticatedUser extractor in handlers

    // Build our application with both public and protected routes
    let app = Router::new()
        .merge(public_routes)
        .merge(protected_routes)
        .layer(
            ServiceBuilder::new()
                .layer(TraceLayer::new_for_http())
                .layer(
                    CorsLayer::new()
                        .allow_origin(Any)
                        .allow_methods([
                            Method::GET,
                            Method::POST,
                            Method::PUT,
                            Method::DELETE,
                            Method::OPTIONS,
                        ])
                        .allow_headers(Any),
                )
                .layer(DefaultBodyLimit::max(100 * 1024 * 1024)), // 100MB max file size
        )
        .with_state(app_state);

    // Run server
    let addr_str = format!("{}:{}", config.server.host, config.server.port);
    let addr: SocketAddr = addr_str.parse().expect("Invalid server address");
    let listener = tokio::net::TcpListener::bind(addr).await?;

    info!("Server listening on {}", addr);
    axum::serve(listener, app).await?;

    Ok(())
}
